package com.postsuperman.proxypool.api;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.log.StaticLog;
import com.postsuperman.proxypool.common.Response;
import com.postsuperman.proxypool.common.enums.ValidateStatusEnum;
import com.postsuperman.proxypool.dao.ProxyDAO;
import com.postsuperman.proxypool.pojo.HttpProxy;
import com.postsuperman.proxypool.service.HttpProxyService;
import com.postsuperman.proxypool.service.ip66.Ip66ProxyService;
import com.postsuperman.proxypool.service.kuai.KuaiDaiLiProxyService;
import com.postsuperman.proxypool.service.proxylistplus.ProxyListPlusProxyService;
import com.postsuperman.proxypool.service.xici.XiciDaiLiProxyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author ： Lwl postserupman
 * @Description TODO
 * @Date 2019/2/27 12:06
 * @Version 1.0
 */
@Api("管理接口")
@RestController
@RequestMapping("ToolApi")
public class ToolApi {

    private static final String VALIDATE_URL = "https://api.ipify.org?format=json";

    @Value("${proxy.sync.ip66.enabled}")
    private Boolean ip66Enabled;

    @Value("${proxy.sync.ip66.total}")
    private Integer ip66Total;

    @Value("${proxy.sync.kuai.enabled}")
    private Boolean kuaiEnabled;

    @Value("${proxy.sync.kuai.total}")
    private Integer kuaiTotal;

    @Value("${proxy.sync.proxylistplus.enabled}")
    private Boolean proxylistplusEnabled;

    @Value("${proxy.sync.proxylistplus.total}")
    private Integer proxylistplusTotal;

    @Value("${proxy.sync.xici.enabled}")
    private Boolean xiciEnabled;

    @Value("${proxy.sync.xici.total}")
    private Integer xiciTotal;

    @Value("${proxy.sync.zm.enabled}")
    private Boolean zmEnabled;

    @Autowired
    private ProxyDAO proxyDAO;

    @ApiOperation("手动执行代理同步操作")
    @GetMapping("Sync")
    public Object proxySync() {
        HttpProxyService httpProxyService = null;

        Integer total = 0;

        if (ip66Enabled) {
            httpProxyService = new Ip66ProxyService();
            httpProxyService.setPage(0, ip66Total);
            List<HttpProxy> httpProxies = httpProxyService.get();
            System.out.println("同步ip66代理：" + httpProxies.size());
            total += httpProxies.size();
            proxyDAO.insertBatch(httpProxies);
        }
        if (kuaiEnabled) {
            httpProxyService = new KuaiDaiLiProxyService();
            httpProxyService.setPage(0, kuaiTotal);
            List<HttpProxy> httpProxies = httpProxyService.get();
            total += httpProxies.size();
            System.out.println("同步快代理：" + httpProxies.size());
            proxyDAO.insertBatch(httpProxies);
        }
        if (proxylistplusEnabled) {
            httpProxyService = new ProxyListPlusProxyService();
            httpProxyService.setPage(0, proxylistplusTotal);
            List<HttpProxy> httpProxies = httpProxyService.get();
            total += httpProxies.size();
            System.out.println("同步proxylistplus代理：" + httpProxies.size());
            proxyDAO.insertBatch(httpProxies);
        }
        if (xiciEnabled) {
            httpProxyService = new XiciDaiLiProxyService();
            httpProxyService.setPage(0, xiciTotal);
            List<HttpProxy> httpProxies = httpProxyService.get();
            total += httpProxies.size();
            System.out.println("同步西刺代理：" + httpProxies.size());
            proxyDAO.insertBatch(httpProxies);
        }

        StaticLog.info("同步任务结束：同步代理总数：{}", total);
        return Response.success(String.format("同步代理总数：%s", total));
    }

    @ApiOperation("手动执行代理校验操作")
    @GetMapping("validate")
    public Object validate() {
        ExecutorService workerGroup = null;

        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failCount = new AtomicInteger(0);
        int pageSize = 200;

        List<HttpProxy> proxies = this.proxyDAO.findAll();
        long count = proxies.size();
        for (int start = 0; start < count; start += pageSize) {
            workerGroup = ThreadUtil.newExecutor();
            int end = (int) Long.min(start + pageSize - 1, count);
            List<HttpProxy> httpProxies = proxies.subList(start, end);
            //代理池
            for (HttpProxy httpProxy : httpProxies) {
                workerGroup.submit(() -> {
                    Integer code = validate(httpProxy);
                    if (ValidateStatusEnum.SUCCESS_CODE.getCode().equals(code)) {
                        successCount.addAndGet(1);
                        //验证可用
                        this.proxyDAO.update(httpProxy);
                    } else if (ValidateStatusEnum.FAIL_CODE.getCode().equals(code)) {
                        failCount.addAndGet(1);
                        //验证失败 代理降级
                        this.proxyDAO.delete(httpProxy);
                    } else {
                        //响应异常 代理移除
                        this.proxyDAO.deleteById(httpProxy.getId());
                    }
                });
            }

            //生产环境不要使用该方式进行统计，避免线程阻塞
            isTheardStop(workerGroup);
        }
        String msg = String.format("代理池校验完毕：总校验代理数：%s,留存代理数：%s,降级代理数：%s,移除代理数：%s", count
                , successCount.get(), failCount.get(), count - successCount.get() - failCount.get());

        StaticLog.info(msg);

        return Response.success(msg);
    }

    @ApiOperation("手动执行清空代理池操作")
    @GetMapping("removeAll")
    public Object removeAll() {
        this.proxyDAO.deleteAll();
        return null;
    }

    /**
     * 验证ip代理可用性
     * <p>
     * 1、低于2s
     * 2、无法正常代理
     * 3、返回异常
     *
     * @param httpProxy 代理实体信息
     * @return
     */
    private Integer validate(HttpProxy httpProxy) {
        try {
            HttpRequest request = new HttpRequest(VALIDATE_URL);
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(httpProxy.getIp(), httpProxy.getPort()));
            request.setProxy(proxy);
            Long startTime = System.currentTimeMillis();
            HttpResponse response = request.timeout(10000).executeAsync();
            Long endTime = System.currentTimeMillis();
            httpProxy.setMs(endTime - startTime);
            if (response != null && response.getStatus() == HttpStatus.HTTP_OK) {
                return ValidateStatusEnum.SUCCESS_CODE.getCode();
            }
            return ValidateStatusEnum.FAIL_CODE.getCode();
        } catch (Exception ex) {
        }
        return ValidateStatusEnum.EX_CODE.getCode();
    }

    /**
     * 判断线程是否停止
     *
     * @param workerGroup 线程组
     */
    private static void isTheardStop(ExecutorService workerGroup) {
        workerGroup.shutdown();
        try {
            while (true) {
                if (workerGroup.isTerminated()) {
                    System.out.println("当前线程任务执行完毕");
                    break;
                }

                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
